Physical Window Attributes

From Documentation
DocumentationSmall Talks2010DecemberPhysical Window Attributes
Physical Window Attributes

Author
Diego Bravo, Software Architect, AMERICATI EIRL
Date
December 8, 2010
Version
ZK 5.0


The Problem

A Web application where the end user can have several browser windows or tabs opened at the same time pointing to the same application but dealing with different session data, and:

  1. The user may hit the reload button at any time for whatever reason, without the browser window's information being lost
  2. The application may jump to another zul document via a SendRedirect

What kind of attributes do we need in order to support that? The Session scope would be ok if we just had one physical window (on current browsers several windows share the session attributes.) The ZK Desktop is fine if the page is not reloaded (as it happens with the infamous F5 key on most browsers, or after a redirection.)

What is needed is some kind of intermediate scope, what I call a "physical window scope", in which the attributes are carried from page reloads (like the Session scope) but differentiates between the physical browser windows (like the Desktop scope.)

Implementing the "Physical Window Scope" (PWS)

The PWS must be stored in the session scope (in order to be carried on redirects) but must be indexed by phyisical window. So the PWS is a hash map stored as a special attribute in the session scope.

Also, the most simple way to maintain or pass an attribute between jumps of the same physical browser window is by a URL variable (GET parameter), so we define a URL variable to be passed to every zul document. Its value is a random text. These ideas are implemented in the class PWS.java. The first rule is that the redirections need to add that parameter:

public void onClickForJump() {
		String url = "destination.zul?" + PWS.getPWSParams();
		Executions.getCurrent().sendRedirect(url);
	}

Or the convenience:

public void onClickForJump() {
		PWS.sendRedirect("destination.zul");
	}

Also, every document must get that parameter and store it as an attribute (since the URL parameters get lost after the page loader request thread.) A simple way to do that is using the "onCreate" method:

public void onCreate() {
    PWS.register();
   // more initialization code
}

As an implementation detail, the URL parameter is stored as a Desktop attribute.

Using PSW Attributes

What did we achieve? Now we can set PWS attributes in the following way:

PWS.setAttribute("name", someValue);

And (possibly from another zul) get it with:

PWS.getAttribute("name")


Firing the Physical Windows

There are several methods in order to fire the physically independent windows (or tabs as configured in the browser preferences); for example, from a standard html document just call the classic "window.open()". From a ZUL document (backed with java class) the sendRedirect with two parameters can be applied that way:

Executions.getCurrent().sendRedirect("some-page.zul", "_blank");

Please consider using the PWS.fireNewWindow(page) method as an alternative in order to support the PWS on the fired windows (see the implementation below.)

The implementation of PWS.java

package com.americati.zkutil;

import java.io.Serializable;
import java.util.Map;

import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;

/**
 * @author Diego Bravo
 */
public class PWS {

	private static String WINDOW_ATTR_KEY = "_window-attributes_";
	private static String WINDOW_ATTR_PARAM_NAME = "zkattrwinkey";
	
	public static void register() {
		// if we have a parameter in the URL, use it; else, create a new one
		String windowID = Executions.getCurrent().getParameter(WINDOW_ATTR_PARAM_NAME);
		if(windowID == null) {
			windowID = getNewWindowID();
		}
		Executions.getCurrent().getDesktop().setAttribute(WINDOW_ATTR_KEY, windowID);
	}
	
	// a dummy random id
	private static String getNewWindowID() {
		long t = new java.util.Date().getTime();
		int r = (int)(Math.random()*10000);
		return t + "" + r;
	}
	
	public static String getPWSParams() {
		return WINDOW_ATTR_PARAM_NAME + "=" + getCurrentPWSID();
	}
	
	public static void fireNewWindow(String destination) {
		Executions.getCurrent().sendRedirect(destination + "?" + WINDOW_ATTR_PARAM_NAME + "=" + getNewWindowID(), "_blank");
	}
	
	public static void sendRedirect(String destination) {
		Executions.getCurrent().sendRedirect(destination + "?" + getPWSParams());
	}
	
	@SuppressWarnings("unchecked")
	private static Map <String, Map<String,Serializable>> getAttributeMap() {
		Map <String, Map<String,Serializable>> attrMap; 
		attrMap = (Map<String, Map<String,Serializable>>)Executions.getCurrent().getSession().getAttribute(WINDOW_ATTR_KEY);
		if(attrMap == null) {
			attrMap = new java.util.HashMap<String, Map<String,Serializable>>();
			Executions.getCurrent().getSession().setAttribute(WINDOW_ATTR_KEY, attrMap);
		}
		return attrMap;
	}
	
	private static String getCurrentPWSID() {
		Desktop d = Executions.getCurrent().getDesktop();
		String winID = (String)d.getAttribute(WINDOW_ATTR_KEY);
		if(winID == null) {
			register();
			winID = (String)d.getAttribute(WINDOW_ATTR_KEY);
		}
		return winID;
	}
	
	private static Map<String, Serializable> getPWSAttributeMap(String winID) {
		Map <String, Map<String,Serializable>> attrMap = getAttributeMap();
		Map<String,Serializable> attrWindowMap = attrMap.get(winID);
		if(attrWindowMap == null) {
			attrWindowMap = new java.util.HashMap<String,Serializable>();
			attrMap.put(winID, attrWindowMap);
		}
		return attrWindowMap;
	}
	
	public static void setAttribute(String name, Serializable obj) {
		String winID = getCurrentPWSID();
		getPWSAttributeMap(winID).put(name, obj);
	}

	public static Serializable getAttribute(String name) {
		String winID = getCurrentPWSID();
		return getPWSAttributeMap(winID).get(name);
	}
}


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.